package cloud.pangui.core.mongo.element;

import cloud.pangui.core.mongo.utils.SqlWhereParseUtils;
import com.mongodb.MongoException;
import org.bson.Document;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * 条件表达式
 *
 * @Auther hepangui
 * @Date 2020/10/06 0001
*/
public class Cond extends Func {

	/**
	 * or操作符
	 */
	public static final String OR = "$or";

	/**
	 * and操作符
	 */
	public static final String AND = "$and";

	/**
	 * 等于
	 */
	public static final String EQ = "$eq";

	/**
	 * 不等于
	 */
	public static final String NE = "$ne";

	/**
	 * in
	 */
	public static final String IN = "$in";

	/**
	 * not in
	 */
	public static final String NOTIN = "$nin";

	/**
	 * 大于
	 */
	public static final String GT = "$gt";

	/**
	 * 大于等于
	 */
	public static final String GTE = "$gte";

	/**
	 * 小于
	 */
	public static final String LT = "$lt";

	/**
	 * 小于等于
	 */
	public static final String LTE = "$lte";

	/**
	 * 正则匹配
	 */
	public static final String REG = "$regex";

	/**
	 * 存在
	 */
	public static final String EXISTS = "$exists";

	/**
	 * 非
	 */
	private static final String NOT = "$not";

	/**
	 * 简单条件时，对应的属性值
	 */
	private String key;

	/**
	 * 简单条件时，对应的value
	 */
	private Object value;

	/**
	 * 简单条件时，对应的符号
	 */
	private String symbol;

	/**
	 * @param key    键
	 * @param symbol 比较符
	 * @param value  值
	 */
	private Cond(String key, String symbol, Object value) {
		this(Field.of(key), symbol, Value.of(value));
	}

	private Cond(Ele key, String symbol, Ele value) {
		// 简单表达式
		if (key instanceof Field && value instanceof Value) {
			this.key = ((Field) key).getKey();
			this.value = value.toExpression();
			this.symbol = symbol;
		}
		this.doc = new Document(symbol,
				Arrays.asList(new Object[]{key.toExpression(), value.toExpression()}));
	}

	private Cond() {

	}

	/**
	 * 从一个表达式获取一个条件
	 *
	 * @param s
	 * @return
	 */
	public static Cond ofString(String s) {
		return SqlWhereParseUtils.parseWhereEle(s);
	}


	/**
	 * 简单形式的表达式
	 *
	 * @return
	 */
	public Document toSimpleExpression() {
		if (this.key == null) {
			throw new MongoException("复杂条件不能输出简单表达式");
		}
		if (EQ.equals(symbol)) {
			return new Document(key, value);
		}
		return new Document(this.key, new Document(this.symbol, this.value));
	}

	@Override
	public Document toExpression() {
		return this.doc;
	}

	/**
	 * 返回相等比较
	 */
	public static Cond eq(Ele key, Ele value) {
		return new Cond(key, EQ, value);
	}

	/**
	 * 返回不相等比较
	 */
	public static Cond ne(Ele key, Ele value) {
		return new Cond(key, NE, value);
	}

	/**
	 * 返回>比较
	 */
	public static Cond gt(Ele key, Ele value) {
		return new Cond(key, GT, value);
	}

	/**
	 * 返回>=比较
	 */
	public static Cond gte(Ele key, Ele value) {
		return new Cond(key, GTE, value);
	}

	/**
	 * 返回<比较
	 */
	public static Cond lt(Ele key, Ele value) {
		return new Cond(key, LT, value);
	}

	/**
	 * 返回<=比较
	 */
	public static Cond lte(Ele key, Ele value) {
		return new Cond(key, LTE, value);
	}

	/**
	 * 返回in比较
	 */
	public static Cond in(Ele key, Ele values) {
		if (!(values instanceof Value)) {
			throw new MongoException("in操作内不允许使用任何函数");
		}
		return new Cond(key, IN, values);
	}

	/**
	 * 返回notin比较
	 */
	public static Cond notin(Ele key, Ele values) {
		if (!(values instanceof Value)) {
			throw new MongoException("not in操作内不允许使用任何函数");
		}
		Cond cond = new Cond();
		cond.doc(new Document(NOT, new Cond(key, IN, values).toExpression()));
		if (key instanceof Field && values instanceof Value) {
			cond.key = ((Field) key).getKey();
			cond.value = values.toExpression();
			cond.symbol = NOTIN;
		}
		return cond;
	}

	/**
	 * 返回相等比较
	 */
	public static Cond eq(String key, Object value) {
		return new Cond(key, EQ, value);
	}

	public static Cond isNull(Ele key) {
		return new Cond(key, EQ, Value.of(null));
	}

	public static Cond isNotNull(String key) {
		return new Cond(key, NE, Value.of(null));
	}

	public static Cond isNull(String key) {
		return new Cond(key, EQ, Value.of(null));
	}

	public static Cond isNotNull(Ele key) {
		return new Cond(key, NE, Value.of(null));
	}

	/**
	 * 返回不相等比较
	 */
	public static Cond ne(String key, Object value) {
		return new Cond(key, NE, value);
	}

	/**
	 * 返回>比较
	 */
	public static Cond gt(String key, Object value) {
		return new Cond(key, GT, value);
	}

	/**
	 * 返回>=比较
	 */
	public static Cond gte(String key, Object value) {
		return new Cond(key, GTE, value);
	}

	/**
	 * 返回<比较
	 */
	public static Cond lt(String key, Object value) {
		return new Cond(key, LT, value);
	}

	/**
	 * 返回<=比较
	 */
	public static Cond lte(String key, Object value) {
		return new Cond(key, LTE, value);
	}

	/**
	 * 返回in比较
	 */
	public static Cond in(String key, Object[] values) {
		List<Object> list = new LinkedList<>();
		for (Object value : values) {
			list.add(value);
		}
		return in(Field.of(key), Value.of(list));
	}

	/**
	 * 返回notin比较
	 */
	public static Cond notin(String key, Object[] values) {
		List<Object> list = new LinkedList<>();
		for (Object value : values) {
			list.add(value);
		}
		return notin(Field.of(key), Value.of(list));
	}


	public static Cond like(Ele key, Ele value) {
		if (!(value.toExpression() instanceof String)) {
			throw new MongoException("like条件必须是String");
		}
		String s = (String) value.toExpression();
		if (s.charAt(0) == '%' && s.charAt(s.length() - 1) != '%') {
			return reg(key, Value.of(s.substring(1) + "$"));
		} else if (s.charAt(0) != '%' && s.charAt(s.length() - 1) == '%') {
			return reg(key, Value.of("^" + s.substring(0, s.length() - 1)));
		} else if (s.charAt(0) == '%' && s.charAt(s.length() - 1) == '%') {
			return reg(key, Value.of(s.substring(1, s.length() - 1)));
		} else {
			return eq(key, Value.of(s));
		}
	}

	/**
	 * like条件转换，仅支持前置匹配和后置匹配，
	 * 如果需要复杂匹配，请使用正则表达式
	 * %aaa   ->  aaa$
	 * aaa%   ->  ^aaa
	 *
	 * @param key
	 * @param value
	 * @return
	 */
	public static Cond like(String key, Object value) {
		return like(Field.of(key), Value.of(value));
	}

	/**
	 * 返回正则比较
	 */
	public static Cond reg(String key, Object value) {
		return new Cond(key, REG, value);
	}

	/**
	 * 返回正则比较
	 */
	public static Cond reg(Ele key, Ele value) {

		Cond cond = new Cond(key, REG, value);
		cond.doc(new Document("$regexMatch", new Document("input", key.toExpression()).append("regex", value.toExpression())));
		return cond;
	}

	/**
	 * and组合条件，仅用于复杂表达式
	 *
	 * @param eles
	 * @return
	 */
	public static Cond and(Cond... eles) {
		List<Document> list = new ArrayList<>();
		for (Ele ele : eles) {
			list.add((Document) (ele.toExpression()));
		}
		return (Cond) new Cond().doc(new Document(AND, list));
	}

	/**
	 * 或条件  仅用于复杂表达式
	 *
	 * @param eles
	 * @return
	 */
	public static Cond or(Cond... eles) {
		List<Document> list = new ArrayList<>();
		for (Ele ele : eles) {
			list.add((Document) (ele.toExpression()));
		}
		return (Cond) new Cond().doc(new Document(OR, list));
	}

	/**
	 * 根据符号获取一个where表达式
	 *
	 * @param symbol
	 * @param key
	 * @param value
	 * @return
	 */
	public static Cond ofSymbol(String symbol, String key, Object value) {
		return ofSymbol(symbol, Field.of(key), Value.of(value));
	}


	/**
	 * 根据符号获取一个where表达式
	 *
	 * @param symbol
	 * @param key
	 * @param value
	 * @return
	 */
	public static Cond ofSymbol(String symbol, Ele key, Ele value) {
		switch (symbol) {
			case "=":
				return eq(key, value);
			case "<>":
			case "!=":
				return ne(key, value);
			case ">=":
				return gte(key, value);
			case "<=":
				return lte(key, value);
			case ">":
				return gt(key, value);
			case "<":
				return lt(key, value);
			case "in":
			case "IN":
				return in(key, value);
			case "not in":
			case "NOT IN":
				return notin(key, value);
			case "like":
			case "LIKE":
				return like(key, value);
		}
		return new Cond(key, symbol, value);
	}

	/**
	 * 是否是一个简单表法师
	 *
	 * @return
	 */
	public boolean isSimple() {
		return this.key != null;
	}

	/**
	 * 获取key，只有简单表达式可以获取
	 *
	 * @return
	 */
	public String getKey() {
		if (this.key == null) {
			throw new MongoException("复杂条件不能输出简单表达式");
		}
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	/**
	 * 获取value，只有简单表达式可以获取
	 *
	 * @return
	 */
	public Object getValue() {
		if (this.key == null) {
			throw new MongoException("复杂条件不能输出简单表达式");
		}
		return value;
	}

	public void setValue(Object value) {
		this.value = value;
	}

	/**
	 * 获取符号，只有简单表达式可以获取
	 *
	 * @return
	 */
	public String getSymbol() {
		if (this.key == null || this.value == null) {
			throw new MongoException("复杂条件不能输出简单表达式");
		}
		return symbol;
	}

	public void setSymbol(String symbol) {
		this.symbol = symbol;
	}

	/**
	 * 判断是不是1=1 这种
	 *
	 * @return
	 */
	public boolean alwaysTrue() {
		if (this.key == null && this.doc != null) {
			Object e = this.doc.get(EQ);
			if (e != null && e instanceof List) {
				List list = (List) e;
				if (list.size() == 2) {
					return list.get(0) != null && list.get(0).equals(list.get(1));
				}
			}
		}
		return false;
	}
}
